﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Hosting;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using Vit.Data;
using Vit.Web.Mvc;
using Vit.Web.Services;
using Vit.Caching;
using Vit.Web.Mvc.Filters;
using Vit.Web.DataAnnotations;
using Vit.Web.Models;

namespace Vit.Web
{
    public class VitApplication : HttpApplication
    {
        public VitApplication()
        {
        }

        protected void Application_Start()
        {
            // register adapter to data annotations for customized attributes 
            // by DataAnnotationsModelValidatorProvider.RegisterAdapter
            // to enable client side validation
            DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredAttribute), typeof(RequiredModelValidator));
            DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RangeAttribute), typeof(RangeModelValidator));
            DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(StringLengthAttribute), typeof(StringLengthModelValidator));
            DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RegularExpressionAttribute), typeof(RegularExpressionModelValidator));

            // set custom DataAnnotationsModelMetadataProvider
            ModelMetadataProviders.Current = new DataAnnotationsModelMetadataProvider2();

            UnityContainer = SetupContainer();

            Reload();
        }

        protected void Application_End()
        {
            IEnumerable<IModule> components = UnityContainer.ResolveAll<IModule>();
            foreach (IModule component in components)
                component.Unload();
        }

        protected void Application_BeginRequest(object sender, EventArgs e)
        {
        }

        public void Reload()
        {
            ControllerBuilder.Current.SetControllerFactory(UnityContainer.Resolve<VitControllerFactory>());
            ViewEngines.Engines.Clear();
            ViewEngines.Engines.Add(UnityContainer.Resolve<IViewEngine>());

            RouteCollection routes = UnityContainer.Resolve<RouteCollection>();
            IFilterRegistry filterRegistry = UnityContainer.Resolve<FilterRegistry>();
            ModelBinderDictionary modelBinders = UnityContainer.Resolve<ModelBinderDictionary>();
            routes.Clear();
            filterRegistry.Clear();
            modelBinders.Clear();

            filterRegistry.Add(Enumerable.Empty<IFilterCriteria>(), typeof(AntiForgeryAuthorizationFilter));

            // start the module and plugins if any.
            IEnumerable<IModule> components = UnityContainer.ResolveAll<IModule>();
            foreach (IModule component in components)
            {
                component.RegisterWithContainer();
                component.Initialize();
                component.RegisterFilters(filterRegistry);
                component.RegisterModelBinders(modelBinders);

                UnityContainer.RegisterInstance(component);
            }

            RegisterRoutes(RouteTable.Routes);
        }

        private IUnityContainer SetupContainer()
        {
            IUnityContainer parentContainer = new UnityContainer();
            parentContainer
                .RegisterInstance(RouteTable.Routes)
                .RegisterInstance(ModelBinders.Binders)
                .RegisterInstance(ViewEngines.Engines)
                .RegisterInstance(HostingEnvironment.VirtualPathProvider);

            foreach (ConnectionStringSettings connectionString in ConfigurationManager.ConnectionStrings)
            {
                parentContainer.RegisterInstance(connectionString.Name, connectionString.ConnectionString);
            }

            WebCacheManager webCache = new WebCacheManager();
            webCache.Initialize("WebCache");
            EntLibCacheMananger localizationCache = new EntLibCacheMananger();
            localizationCache.Initialize("LocalizationCache");
            EntLibCacheMananger dataCache = new EntLibCacheMananger();
            dataCache.Initialize("DataCache");

            parentContainer
                .RegisterType<IFilterRegistry, FilterRegistry>(new ContainerControlledLifetimeManager())
                .RegisterType<IActionInvoker, VitControllerActionInvoker>(new ContainerControlledLifetimeManager())
                .RegisterType<IViewEngine, VitViewEngine>(new ContainerControlledLifetimeManager())
                .RegisterType<IPluginService, PluginService>(new ContainerControlledLifetimeManager())
                .RegisterType<ISiteService, SiteService>(new ContainerControlledLifetimeManager())
                .RegisterType<IAccountService, AccountService>(new ContainerControlledLifetimeManager())
                .RegisterType<IThemeService, ThemeService>()
                .RegisterType<IValidationService, ValidationService>()
                .RegisterType<ILogService, LogService>()
                .RegisterType<IValidationService, ValidationService>()
                .RegisterType<IDataSourceService, DataSourceService>()
                .RegisterInstance<ICacheManager>("WebCache", webCache)
                .RegisterInstance<ICacheManager>("LocalizationCache", localizationCache)
                .RegisterInstance<ICacheManager>("DataCache", dataCache)
                .RegisterType<MetadataTypeAccessor>(new ContainerControlledLifetimeManager(),
                    new InjectionConstructor(
                        new ResolvedParameter<ICacheManager>("WebCache")
                ))
                .RegisterType<WebSite>(new FactoryMethodLifetimeManager(() => HttpContext.Current.Items["site"] as WebSite ?? parentContainer.Resolve<ISiteService>().GetCurrentSite()))
                .RegisterType<ILocalizationService, LocalizationService>(
                    new InjectionConstructor(
                        new ResolvedParameter<ISiteService>(),
                        new ResolvedParameter<IAccountService>(),
                        new ResolvedParameter<ICacheManager>("LocalizationCache")
                ))
                .RegisterType<IDbConnectionVisitor, NLogConnectionVisitor>("NLogConnectionVisitor")
                .RegisterType<IDbConnectionVisitor, CachingConnectionVisitor>("CachingConnectionVisitor",
                    new InjectionConstructor(
                        new ResolvedParameter<ICacheManager>("DataCache")
                ));

            IUnityContainer childContainer = parentContainer.CreateChildContainer();

            UnityConfigurationSection config = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
            config.Containers.Default.Configure(childContainer);

            childContainer.RegisterInstance(childContainer);

            return childContainer;
        }

        private void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            AreaRegistration.RegisterAllAreas();

            routes.MapRoute(
                "Main",                                                 // Route name
                "{controller}/{action}/{id}",                           // URL with parameters
                new { controller = "Home", action = "Index", id = "" },  // Parameter defaults
                new string[] { "Vit.WebSite.Controllers" }
            );

            foreach (Route route in routes)
                route.RouteHandler = new Vit.Web.Mvc.SiteRouteHandler();
        }

        public IUnityContainer UnityContainer
        {
            get
            {
                return Application["UnityContainer"] as IUnityContainer;
            }
            private set
            {
                Application["UnityContainer"] = value;
            }
        }

        public WebSite CurrentSite
        {
            get
            {
                return this.Context.Items["site"] as WebSite;
            }
            internal set
            {
                this.Context.Items["site"] = value;
            }
        }

        public static VitApplication Current
        {
            get { return HttpContext.Current.ApplicationInstance as VitApplication; }
        }
    }
}
